home *** CD-ROM | disk | FTP | other *** search
/ Enter 2005 August / EnterCD 08_2005.iso / dosapps / pci / PCI.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  2005-03-16  |  66.3 KB  |  2,451 lines

  1. Program PCI;
  2.  
  3. {$G+}
  4. {$R-}
  5. {$S-}
  6. {$I+}
  7. {$N+}
  8. {$E+}
  9. {$Q-}
  10.  
  11.  
  12.  
  13. uses newdelay,dos,crt;
  14.  
  15.  
  16. {$I classes.pas}
  17.  
  18.  
  19. {
  20.   This code is Written by Craig Hart in 1996-2005. It is released as freeware;
  21.   please use and modify at will. No guarantees are made or implied.
  22.  
  23.  
  24.   Please read the accompaning documentation PCI.DOC for all the info
  25.   relating to this program!
  26. }
  27.  
  28.  
  29.  
  30. const
  31.   revision    : string[5]='1.1';
  32.  
  33. type treedata =
  34.   record
  35.     b,
  36.     d,
  37.     f    : byte;
  38.     vid,
  39.     did    : word;
  40.   end;
  41.  
  42. var
  43.   olb,
  44.   lastd,
  45.   lastb,
  46.   agpbusnum,
  47.   lw,
  48.   wrlncount,
  49.   PCIverhi,
  50.   PCIverlo,
  51.   PCIchar,
  52.   PCI_hibus,
  53.   errcode,
  54.   deviceid,
  55.   func,
  56.   info,
  57.   lb,
  58.   bus,
  59.   sum,
  60.   disp,
  61.   cap_ptr,
  62.   max        : byte;
  63.  
  64.   showtree,
  65.   pdatareg,
  66.   found,
  67.   businfo,
  68.   userev,
  69.   tableok,
  70.   dorouting,
  71.   dopcirouting,
  72.   summary,
  73.   bogusid,
  74.   genssid,
  75.   dumpregs,
  76.   usebios,
  77.   failed,
  78.   first,
  79.   installermode    : boolean;
  80.  
  81.  
  82.   irqmap    : array[0..15] of byte;
  83.  
  84.   pdata        : array[0..15] of byte;
  85.   pscale    : array[0..15] of byte;
  86.   presult    : array[0..15] of byte;
  87.   
  88.  
  89.   tree        : array[0..199] of treedata;
  90.  
  91.   pmcsr,
  92.   romsize,
  93.   romresult,
  94.   rom_backup    : longint;
  95.  
  96.   spl        : real;
  97.  
  98.   count,
  99.   mb,
  100.   ml,
  101.   conmap,
  102.   len,
  103.   addr,
  104.   index,
  105.   i,
  106.   j,
  107.   l,
  108.   v        : word;
  109.  
  110.   f        : text;
  111.  
  112.   revchk,
  113.   oemidnum,
  114.   oemidstr,
  115.   cmdstr,
  116.   vstr,
  117.   cmpstr    : string;
  118.  
  119.  
  120.   infotbl    : array[0..$ff] of byte;
  121.  
  122.   irqbuff    : array[0..1023] of byte;
  123.  
  124.   linecounter   : word;
  125.   org_output    : pointer;
  126.  
  127.  
  128.   cardbus    : array[1..16] of byte;
  129.   cbu,
  130.   cardptr    : byte;
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149. procedure pagefilter1(var t:text);assembler;
  150. asm
  151.   push ax
  152.   push di
  153.   push es
  154.   push cx
  155.  
  156.   les di,[t]
  157.   mov cx,es:[di+TextRec.BufPos]
  158.   les di,es:[di+TextRec.BufPtr]
  159.   cld
  160.   mov al,10
  161.  
  162. @sl:
  163.   jcxz @ret
  164.   dec cx
  165.   scasb
  166.   jne @sl
  167.   inc linecounter
  168.   jmp @sl
  169.  
  170. @ret:
  171.   pop cx
  172.   pop es
  173.   pop di
  174.   pop ax
  175. end;
  176.  
  177. procedure pagefilter2;assembler;
  178. asm
  179.   push ax
  180.   mov ax,WindMax
  181.   shr ax,8
  182.   cmp linecounter,ax
  183.   jb @ret
  184.  
  185.   sub ax,ax
  186.   int $16
  187.  
  188.   mov linecounter,0
  189.  
  190. @ret:
  191.   pop ax
  192. end;
  193.  
  194. procedure page_output_FlushFunc;assembler;
  195. asm
  196.   push es
  197.   push bx
  198.   call pagefilter1
  199.   push es
  200.   push bx
  201.   call [org_output]
  202.   call pagefilter2
  203.   retf 4
  204. end;
  205.  
  206.  
  207.  
  208.  
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.  
  216. function cvtb(b:byte) : byte;
  217. begin
  218.   if b>9 then cvtb:=b+ord('A')-10 else cvtb:=b+ord('0');
  219. end;
  220.  
  221. function wrhexb(byt:byte): string;
  222. begin
  223.  wrhexb:=chr(cvtb(byt and $0f));
  224. end;
  225.  
  226. function wrhex(byt:byte) : string;
  227. begin
  228.   wrhex:=chr(cvtb((byt and $f0) shr 4))+chr(cvtb(byt and $0f));
  229. end;
  230.  
  231. function wrhexw(wor:word): string;
  232. begin
  233.   wrhexw:=chr(cvtb(wor shr 12))+chr(cvtb((wor shr 8) and $f))+chr(cvtb((wor shr 4) and $f))+chr(cvtb(wor and $f));
  234. end;
  235.  
  236.  
  237. (* Make the PCI configuration status register printout pretty *)
  238. (* Input = the string to be output *)
  239.  
  240. Procedure printstatus (s : string);
  241. Begin
  242.   if not first then if (length(s)+wherex)>78 then
  243.   begin
  244.     writeln(',');
  245.     write('   ');
  246.   end else write(', ');
  247.   write(s);
  248.   first:=false;
  249. End;
  250.  
  251.  
  252. function IORedirected : boolean ; Assembler;
  253. asm
  254.   push ds
  255.   mov ax,prefixseg
  256.   mov ds,ax
  257.   xor bx,bx
  258.   les bx,[bx + $34]
  259.   mov al,es:[bx]
  260.   mov ah,es:[bx +1]
  261.   pop ds
  262.   cmp al,ah
  263.   mov al,true
  264.   jne @exit
  265.  
  266.   mov al,false
  267.  
  268.  @exit:
  269. end;
  270.  
  271. function lookup_bios(deviceid,func,bus:byte;index:word) : byte;
  272.  
  273. var inf:byte;
  274.  
  275. begin
  276.   asm
  277.     mov ax,$b108
  278.     mov bl,deviceid
  279.     shl bl,3
  280.     add bl,func
  281.     mov bh,bus
  282.     mov di,index
  283.     int $1a
  284.     jc @exit
  285.  
  286.     mov failed,false
  287.     mov inf,cl
  288.   @exit:
  289.     mov errcode,ah
  290.   end;
  291.   lookup_bios:=inf;
  292. end;
  293.  
  294.  
  295. function lookup_hw(deviceid,func,bus:byte;index:word) : byte;
  296. var inf:byte;
  297.  
  298. begin
  299.   asm
  300.     mov ax,$8000
  301.     mov al,bus
  302.     db $66;shl ax,16
  303.  
  304.     mov ax,index
  305.     and ax,00fch
  306.     mov ah,deviceid
  307.     shl ah,3
  308.     add ah,func
  309.  
  310.     mov dx,0cf8h
  311.     db $66;out dx,ax
  312.  
  313.     mov ax,index
  314.     and ax,3
  315.     mov bl,8
  316.     mul bl
  317.     mov cx,ax
  318.  
  319.     mov dx,0cfch
  320.     db $66;in ax,dx
  321.     db $66;shr ax,cl
  322.     mov inf,al
  323.     mov failed,false
  324.  
  325.  
  326.     db $66;xor ax,ax
  327.     mov dx,0cf8h
  328.     db $66;out dx,ax
  329.  
  330.   end;
  331.   lookup_hw:=inf;
  332. end;
  333.  
  334.  
  335. procedure write_dword_bios(deviceid,func,bus:byte;index,datah,datal:word);
  336. begin
  337.   asm
  338.     mov ax,$b10d
  339.     mov bl,deviceid
  340.     shl bl,3
  341.     add bl,func
  342.     mov bh,bus
  343.     mov di,index
  344.     mov cx,datah
  345.     db $66; rol cx,16
  346.     mov cx,datal
  347.     int $1a
  348.     mov errcode,ah
  349.   end;
  350. end;
  351.  
  352. procedure write_dword_hw(deviceid,func,bus:byte;index,datah,datal:word);
  353. begin
  354.   asm
  355.     mov ax,$8000
  356.     mov al,bus
  357.     db $66;shl ax,16
  358.  
  359.     mov ax,index
  360.     and ax,00fch
  361.     mov ah,deviceid
  362.     shl ah,3
  363.     add ah,func
  364.  
  365.     mov dx,0cf8h
  366.     db $66;out dx,ax
  367.  
  368.     mov ax,index
  369.     and ax,3
  370.     mov bl,8
  371.     mul bl
  372.     mov cx,ax
  373.  
  374.     mov ax,datah
  375.     db $66;shl ax,16
  376.     mov ax,datal
  377.  
  378.     mov dx,0cfch
  379.     db $66;out dx,ax
  380.     mov failed,false
  381.  
  382.     db $66;xor ax,ax
  383.     mov dx,0cf8h
  384.     db $66;out dx,ax
  385.   end;
  386. end;
  387.  
  388. procedure listmap(va:word;dispst:string);
  389. var
  390.   comma     : byte;
  391.   failed : boolean;
  392.   l,
  393.   j     : word;
  394.  
  395. begin
  396.   failed:=true;
  397.   write(dispst);
  398.   comma:=0;
  399.   for l:=0 to 15 do if (va and (1 shl l))>0 then inc(comma);
  400.  
  401.   l:=1;
  402.   j:=0;
  403.   repeat
  404.     if (va and l)=l then
  405.     begin
  406.       write(j);
  407.       if comma>1 then write(',') else write(' ');
  408.       dec(comma);
  409.       failed:=false;
  410.     end;
  411.     l:=l shl 1;
  412.     inc(j);
  413.   until j=16;
  414.   if failed then writeln('None') else writeln;
  415. end;
  416.  
  417.  
  418. procedure lookupven(silent:boolean);
  419. begin
  420.   reset(f);
  421.   failed:=true;
  422.   repeat
  423.     readln(f,vstr);
  424.     if length(vstr)<3 then vstr:=';   oops';
  425.     if (vstr[1]='V') and (copy(vstr,3,4)=cmpstr) then
  426.     begin
  427.       textcolor(14);
  428.       if not silent then write(copy(vstr,8,length(vstr)));
  429.       textcolor(7);
  430.       failed:=false;
  431.     end;
  432.   until eof(f) or not failed;
  433.   if failed and not silent then
  434.   begin
  435.     textcolor(12);
  436.     write('Unknown');
  437.     textcolor(7);
  438.   end;
  439. end;
  440.  
  441. procedure lookupdev;
  442. begin
  443. revchk:='';
  444.   failed:=true;
  445.   if not eof(f) then
  446.   repeat
  447.     readln(f,vstr);
  448.     if length(vstr)<3 then vstr:=';   oops';      
  449.     if (vstr[1]='D') and (copy(vstr,3,4)=cmpstr) then
  450.     begin
  451.      if not eof(f) then readln(f,revchk);
  452.      if revchk[1]='R' then
  453.      repeat
  454.        if wrhex(infotbl[8])=copy(revchk,3,2) then
  455.          vstr:='xxxxxxx'+copy(revchk,6,length(revchk));
  456.        if not eof(f) then readln(f,revchk);
  457.      until revchk[1]<>'R';
  458.      textcolor(14);
  459.      write(copy(vstr,8,length(vstr)));
  460.      failed:=false;
  461.      textcolor(7);
  462.     end;
  463.   until eof(f) or not failed or (vstr[1]='V');
  464.   if failed then
  465.   begin
  466.     textcolor(12);
  467.     write('Unknown');
  468.     textcolor(7);
  469.   end;
  470. end;
  471.  
  472.  
  473. procedure showinstallerinfo;
  474. begin
  475.   write('V:',wrhexw(infotbl[1] shl 8+infotbl[0]),' ');
  476.  
  477.   write('D:',wrhexw(infotbl[3] shl 8+infotbl[2]),' ');
  478.  
  479.   write('S:');
  480.   if infotbl[$e] and $7f=0 then
  481.   begin
  482.     write(wrhexw(infotbl[$2f] shl 8+infotbl[$2e]));
  483.     write(wrhexw(infotbl[$2d] shl 8+infotbl[$2c]),' ');
  484.   end else write('00000000 ');
  485.  
  486.   write('B:',bus,' ');
  487.  
  488.   write('E:');
  489.   if deviceid<10 then write('0');
  490.   write(deviceid,' ');
  491.  
  492.   write('F:',func,' ');
  493.  
  494.   write('I:');
  495.   if (infotbl[$3c]<16) and (infotbl[$3c]>0) then
  496.   begin
  497.     if infotbl[$3c]<10 then write('0');
  498.     write(infotbl[$3c],' ');
  499.   end else write('00 ');
  500.  
  501.   write('N:');
  502.   if (infotbl[$3c]<16) and (infotbl[$3c]>0) then
  503.   begin
  504.     if infotbl[$3d]=0 then write('- ') else write(chr(infotbl[$3d]+64),' ');
  505.   end else write('- ');
  506.  
  507.   write('C:');
  508.   write(wrhex(infotbl[$b]),' ');
  509.  
  510.   write('U:');
  511.   write(wrhex(infotbl[$a]),' ');
  512.  
  513.   write('P:');
  514.   write(wrhex(infotbl[$9]),' ');
  515.  
  516.   write('R:');
  517.   write(wrhex(infotbl[$8]),' ');
  518.  
  519.   writeln;
  520. end;
  521.  
  522.  
  523. procedure showroutinginfo;
  524. begin
  525.   writeln('ROM PCI IRQ routing table Windows 9x Compatibility Tests....');
  526.  
  527.  
  528. { Find table }
  529.   i:=0;
  530.   failed:=true;
  531.   repeat
  532.    if (memw[$f000:i]=$5024) and (memw[$f000:i+2]=$5249) then failed:=false;
  533.    if failed then i:=i+16;
  534.   until (i>$ffef) or not failed;
  535.  
  536.  
  537. { check table }
  538.   if not failed then
  539.   begin
  540.     tableok:=true;
  541.  
  542.     writeln(' ROM IRQ routing table found at F000h:',wrhexw(i),'h');
  543.     write(' Table Version ',mem[$f000:i+5],'.',mem[$f000:i+4]);
  544.     if (mem[$f000:i+5]=1) and (mem[$f000:i+4]=0) then writeln(' - OK') else
  545.     begin
  546.       textcolor(12);
  547.       writeln('Invalid Version!');
  548.       textcolor(7);
  549.       tableok:=false;
  550.     end;
  551.  
  552.     write(' Table size ',memw[$f000:i+6],' bytes - ');
  553.     if (memw[$f000:i+6]<33) or (memw[$f000:i+6] mod 16<>0) then
  554.     begin
  555.       textcolor(12);
  556.       writeln('Invalid Size!');
  557.       textcolor(7);
  558.       tableok:=false;
  559.     end else writeln('OK');
  560.  
  561.  
  562.  
  563.     if tableok then
  564.     begin
  565.       write(' Table Checksum ',wrhex(mem[$f000:i+31]),'h - ');
  566.       {$R-}  {Range checking off as sum is DELIBERATELY meant to overfow }
  567.       sum:=0;
  568.       for l:=0 to memw[$f000:i+6]-1 do
  569.       begin
  570.     sum:=sum+mem[$f000:i+l];
  571.       end;
  572.       {$R+}
  573.       if sum=0 then writeln('OK') else
  574.       begin
  575.     textcolor(12);
  576.     writeln('Failed!');
  577.     textcolor(7);
  578.     tableok:=false;
  579.       end;
  580.     end;
  581.  
  582.  
  583.     listmap(memw[$f000:i+10],' IRQ''s dedicated to PCI : ');
  584.  
  585.     if tableok then
  586.     begin
  587.       textcolor(10);
  588.       writeln(' The ROM PCI IRQ routing table appears to be OK.');
  589.       textcolor(7);
  590.     end else
  591.     begin
  592.       textcolor(12);
  593.       writeln(' The ROM PCI IRQ routing table appears to be faulty!!');
  594.       textcolor(7);
  595.     end;
  596.  
  597.   end else
  598.   begin
  599.     textcolor(12);
  600.     writeln('No ROM PCI IRQ routing table found!!!');
  601.     textcolor(7);
  602.   end;
  603. end;
  604.  
  605.  
  606. procedure dohexdump;
  607. begin
  608.   writeln('Hex-Dump of IRQ Routing table : ');
  609.   writeln;
  610.   write('  0000  ');
  611.   for i:=0 to 1023 do
  612.   begin
  613.     if (i>0) and (i mod 16=0) then
  614.     begin
  615.       write('   ');
  616.       for j:=i-16 to i-1 do if ord(irqbuff[j])<32 then write('.') else write(chr(irqbuff[j]));
  617.       writeln;
  618.       write('  ',wrhexw(i),'  ');
  619.     end;
  620.     write(wrhex(irqbuff[i]),' ');
  621.   end;
  622.   write('   ');
  623.   for j:=1008 to 1023 do if ord(irqbuff[j])<32 then write('.') else write(chr(irqbuff[j]));
  624.   writeln;
  625.   writeln;
  626. end;
  627.  
  628.  
  629. procedure docapdecode;
  630. var i : word;
  631. begin
  632.   writeln(' New Capabilities List Present:');
  633. {type 0}     if infotbl[$e] and $7f=0 then cap_ptr:=infotbl[$34];
  634. {type 1}     if infotbl[$e] and $7f=1 then cap_ptr:=infotbl[$34];
  635. {type 2}     if infotbl[$e] and $7f=2 then cap_ptr:=infotbl[$14];
  636.  
  637.   if cap_ptr<>0 then
  638.   repeat
  639.     case infotbl[cap_ptr] of
  640.  
  641.       01 : begin
  642.          write('   Power Management Capability, Version ');
  643.          if infotbl[cap_ptr+2]and 7=3 then writeln('1.2') else
  644.          if infotbl[cap_ptr+2]and 7=2 then writeln('1.1') else
  645.          if infotbl[cap_ptr+2]and 7=1 then writeln('1.0') else
  646.          begin
  647.            textcolor(12);
  648.            writeln('Unknown: Code ',wrhexb(infotbl[cap_ptr+2]and 7),'h');
  649.            textcolor(7);
  650.          end;
  651.  
  652.  
  653. { list supported low-power states; D3 and D0 are always supported}
  654.          if infotbl[cap_ptr+3] and 2=2 then writeln('     Supports low power State D1');
  655.          if infotbl[cap_ptr+3] and 4=4 then writeln('     Supports low power State D2');
  656.          if infotbl[cap_ptr+3] and 6=0 then writeln('     Does not support low power State D1 or D2');
  657.  
  658. {list PME# generation capabilities}         
  659.          if (infotbl[cap_ptr+3] shr 3) = 0 then writeln('     Does not support PME# signalling') else
  660.          begin
  661.            write('     Supports PME# signalling from mode(s) ');
  662.            first:=true;
  663.            if (infotbl[cap_ptr+3] shr 3) and 1=1 then printstatus('D0');
  664.            if (infotbl[cap_ptr+3] shr 3) and 2=2 then printstatus('D1');
  665.            if (infotbl[cap_ptr+3] shr 3) and 4=4 then printstatus('D2');
  666.            if (infotbl[cap_ptr+3] shr 3) and 8=8 then printstatus('D3hot');
  667.            if (infotbl[cap_ptr+3] shr 3) and 16=16 then printstatus('D3cold');
  668.            writeln;
  669.            
  670.            write('     PME# signalling is currently ');
  671.            if infotbl[cap_ptr+5] and 1=1 then writeln('enabled') else writeln('disabled');
  672.           end;
  673.          
  674.  
  675.          write('     Current Power State : D');
  676.          case infotbl[cap_ptr+4] and 3 of
  677.            0 : writeln('0 (Device operational, no power saving)');
  678.            1 : writeln('1 (Device idle, minimum power saving)');
  679.            2 : writeln('2 (Device CLK stopped, medium power saving)');
  680.            3 : writeln('3hot (Device off: no power to device, maximum power saving)');
  681.          end;
  682.  
  683.  
  684. { try to read the power levels, but only if card not asleep now. }
  685.          if infotbl[cap_ptr+4] and 3=0 then 
  686.          begin
  687. { backup registers we are going to mess with }     
  688.          pmcsr:=infotbl[cap_ptr+7] shl 24 + infotbl[cap_ptr+6] shl 16 + infotbl[cap_ptr+5] shl 8 + infotbl[cap_ptr+4];
  689.  
  690.          for i:=0 to 15 do
  691.          begin
  692.            write_dword_hw(deviceid,func,bus,cap_ptr+4,0000,(((infotbl[$e1] shl 8) + infotbl[$e0]) and $0103) + (i shl 9));
  693.            presult[i]:=(lookup_hw(deviceid,func,bus,cap_ptr+5) shr 1) and $f;
  694.            pdata[i]:=lookup_hw(deviceid,func,bus,cap_ptr+7);
  695.            pscale[i]:=(lookup_hw(deviceid,func,bus,cap_ptr+5) shr 5) and 3;
  696.          end;
  697.  
  698. { restore original values to registers regardless }
  699.          write_dword_hw(deviceid,func,bus,cap_ptr+4,pmcsr shr 16,pmcsr and $ffff);
  700.  
  701. { is the reg valid ? }
  702.          pdatareg:=false;
  703.          for i:=0 to 15 do if (pdata[i]<>0) or (pscale[i]<>0) then pdatareg:=true;
  704.          
  705.          if pdatareg then
  706.          begin
  707.            writeln('     Power Data Registers Information:');
  708.  
  709. { debug: for i:=0 to 15 do writeln('Index: ',i,', Data: ',pdata[i],', Scale: ',pscale[i]);}
  710.  
  711.  
  712.  
  713.            if infotbl[$e] and $80=0 then max:=7 else max:=8;
  714.            for i:=0 to max do
  715.            begin
  716.              case i of
  717.                0..3 : write('      D',i,' Power Consumed: ');
  718.                4..7 : write('      D',i-4,' Power Dissipated: ');
  719.                8 :  write('      Common logic Power Consumed: ');
  720.              end;
  721.              case pscale[i] of
  722.                0 : write('???');
  723.                1 : write(pdata[i]*100);
  724.                2 : write(pdata[i]*10);
  725.                3 : write(pdata[i]);
  726.              end;
  727.              writeln('mW');
  728.            end;
  729.          end else
  730.          begin
  731.            if (infotbl[cap_ptr+3] shr 3) and 16=16 then 
  732.            begin
  733.              write('     3.3v AUX Current required : ');
  734.              case ((infotbl[cap_ptr+3] and 1) shl 2) + (infotbl[cap_ptr+2] shr 6) of
  735.                0 : writeln('0mA (Self powered)');
  736.                1 : writeln('55mA');
  737.                2 : writeln('100mA');
  738.                3 : writeln('160mA');
  739.                4 : writeln('220mA');
  740.                5 : writeln('270mA');
  741.                6 : writeln('320mA');
  742.                7 : writeln('375mA');
  743.              end;
  744.            end;
  745.          end;
  746.          end;
  747.        end;
  748.  
  749.  
  750.       02 : begin
  751.          write('   AGP Capability, Version ');
  752.          write(infotbl[cap_ptr+2] shr 4,'.',infotbl[cap_ptr+2] and $0f,' ');
  753.          if (infotbl[cap_ptr+2] shr 4)=1 then writeln('(AGP 1x and/or 2x support)');
  754.          if (infotbl[cap_ptr+2] shr 4)=2 then writeln('(AGP 4x and below support)');
  755.          if (infotbl[cap_ptr+2] shr 4)=3 then
  756.          begin
  757.            write('(AGP 8x and 4x');
  758.            if (infotbl[cap_ptr+2] and $0f)<5 then write(', core register');
  759.            if ((infotbl[cap_ptr+2] and $0f)>4) and
  760.          ((infotbl[cap_ptr+2] and $0f)<10) then write(', appendix register');
  761.            writeln(' support)');
  762.          end;
  763.  
  764.  
  765.  
  766. { Status register }
  767.  
  768.          write('     AGP Speed(s) Supported : ');
  769.          if infotbl[cap_ptr+4] and 8=8 then
  770.          begin
  771.            if infotbl[cap_ptr+4] and 1=1 then write('4x ');
  772.            if infotbl[cap_ptr+4] and 2=2 then write('8x ');
  773.            if infotbl[cap_ptr+4] and 7>3 then write('Unknown Speed Reported (',wrhex(infotbl[cap_ptr+4] and 7),'h)!!');
  774.          end else
  775.          begin
  776.            if infotbl[cap_ptr+4] and 1=1 then write('1x ');
  777.            if infotbl[cap_ptr+4] and 2=2 then write('2x ');
  778.            if infotbl[cap_ptr+4] and 4=4 then write('4x ');
  779.            if infotbl[cap_ptr+4] and 7=0 then
  780.            begin
  781.                  textcolor(12);
  782.          write('None!!');
  783.                  textcolor(11);
  784.          write(' (Assume Only 1x Support)');
  785.                  textcolor(7);
  786.            end;
  787.          end;
  788.          writeln;
  789.  
  790.          write('     FW Transfers Supported : ');
  791.          if infotbl[cap_ptr+4] and $10=$10 then writeln('Yes') else writeln('No');
  792.  
  793.          write('     >4Gb Address Space Supported : ');
  794.          if infotbl[cap_ptr+4] and $20=$20 then writeln('Yes') else writeln('No');
  795.  
  796.          write('     Sideband Addressing Supported : ');
  797.          if infotbl[cap_ptr+5] and 2=2 then writeln('Yes') else writeln('No');
  798.  
  799. { if v3.? reported, see if v3.0 mode is on }
  800.          if (infotbl[cap_ptr+2] shr 4)=3 then
  801.          begin
  802.            write('     AGP v3.0 Operation Mode Available : ');
  803.            if infotbl[cap_ptr+4] and 8=8 then writeln('Yes') else writeln('No');
  804.          end;
  805.  
  806. { isosynch only in AGP v3.0 mode }
  807.          if infotbl[cap_ptr+4] and 8=8 then
  808.          begin
  809.            write('     Isosynchronous Transactions Supported : ');
  810.            if infotbl[cap_ptr+6] and 2=2 then writeln('Yes') else writeln('No');
  811.          end;
  812.  
  813.  
  814.          write('     Maximum Command Queue Length : ',infotbl[cap_ptr+7]+1,' byte');
  815.          if infotbl[cap_ptr+7]=0 then writeln else writeln('s');
  816.  
  817. { Command register }
  818.  
  819.          write('     AGP Speed Selected : ');
  820.          if infotbl[cap_ptr+4] and 8=8 then
  821.          begin
  822.            if infotbl[cap_ptr+8] and 7=1 then write('4x ');
  823.            if infotbl[cap_ptr+8] and 7=2 then write('8x ');
  824.            if infotbl[cap_ptr+8] and 7>2 then write('Unknown Speed Reported (',wrhex(infotbl[cap_ptr+8] and 7),'h)!!');
  825.            if infotbl[cap_ptr+8] and 7=0 then write('None Selected');
  826.          end else
  827.          begin
  828.            if infotbl[cap_ptr+8] and 7=1 then write('1x ');
  829.            if infotbl[cap_ptr+8] and 7=2 then write('2x ');
  830.            if infotbl[cap_ptr+8] and 7=4 then write('4x ');
  831.            if infotbl[cap_ptr+8] and 7=0 then write('None Selected');
  832.          end;
  833.          writeln;
  834.  
  835.          write('     FW Transfers Enabled : ');
  836.          if infotbl[cap_ptr+8] and $10=$10 then writeln('Yes') else writeln('No');
  837.  
  838.          write('     >4Gb Address Space Enabled : ');
  839.          if infotbl[cap_ptr+8] and $20=$20 then writeln('Yes') else writeln('No');
  840.  
  841.          write('     AGP Enabled : ');
  842.          if infotbl[cap_ptr+9] and 1=1 then
  843.          begin
  844.                textcolor(10);
  845.            writeln('Yes');
  846.                textcolor(7);
  847.           end else
  848.           begin
  849.                 textcolor(12);
  850.                 writeln('No');
  851.                 textcolor(7);
  852.           end;
  853.  
  854.          write('     Sideband Addressing Enabled : ');
  855.          if infotbl[cap_ptr+9] and 2=2 then writeln('Yes') else writeln('No');
  856.  
  857.          if infotbl[cap_ptr+4] and 8=8 then
  858.          begin
  859.            write('     AGP v3.0 Operation Mode : ');
  860.            if infotbl[cap_ptr+9] and 1=1 then writeln('Enabled') else writeln('Disabled');
  861.          end;
  862.  
  863.          write('     Current Command Queue Length : ',infotbl[cap_ptr+11]+1,' byte');
  864.          if infotbl[cap_ptr+11]=0 then writeln else writeln('s');
  865.        end;
  866.  
  867.  
  868.       03 : begin
  869.          writeln('   Vital Product Data Capability');
  870.        end;
  871.  
  872.  
  873.       04 : begin
  874.          writeln('   Slot Identification Capability');
  875.  
  876.          write('     This is ');
  877.          if infotbl[cap_ptr+2] and $20=0 then write('not ');
  878.          writeln('a parent bridge');
  879.  
  880.          write('     Number of slots on secondary side of this bridge : ');
  881.          writeln(infotbl[cap_ptr+2] and $1f);
  882.  
  883.          writeln('Chassis Number : ',infotbl[cap_ptr+3]);
  884.        end;
  885.  
  886.  
  887.       05 : begin
  888.          writeln('   Message Signalled Interrupt Capability');
  889.          write('     MSI is ');
  890.          if infotbl[cap_ptr+2] and 1=1 then writeln('enabled') else writeln('disabled');
  891.  
  892.          write('     MSI function can generate ');
  893.          if infotbl[cap_ptr+2] and 128=128 then write('64') else write('32');
  894.          writeln('-bit addresses');
  895.         end;
  896.  
  897.  
  898.       06 : begin
  899.          writeln('   CompactPCI Hot-Swap Capability');
  900.        end;
  901.  
  902.  
  903.       07 : begin
  904.          writeln('   PCI-X Capability');
  905.  
  906. { type 1 }
  907.          if infotbl[$e] and $7f=1 then
  908.          begin
  909.            write('     Secondary AD Bus size is ');
  910.            if infotbl[cap_ptr+2] and 1=1 then write('64') else write('32');
  911.            writeln('-bit');
  912.  
  913.            write('     Secondary AD Bus Maximum Speed in PCI-X mode is ');
  914.            if infotbl[cap_ptr+2] and 2=2 then write('133') else write('66');
  915.            writeln('MHz');
  916.  
  917.            write('     Seconday AD Bus Current Speed : ');
  918.            case ((infotbl[cap_ptr+3] and 1) shl 8 + (infotbl[cap_ptr+2] and $c0) shr 6) of
  919.          0 : write('33');
  920.          1 : write('66');
  921.          2 : write('100');
  922.          3 : write('133');
  923.          4..7 : write('Unknown!');
  924.            end;
  925.  
  926.          end;
  927.  
  928. { type 0 and 1 }
  929.          write('     Primary AD Bus size is ');
  930.          if infotbl[cap_ptr+6] and 1=1 then write('64') else write('32');
  931.          writeln('-bit');
  932.  
  933.          write('     Primary AD Bus Maximum Speed in PCI-X mode is ');
  934.          if infotbl[cap_ptr+6] and 2=2 then write('133') else write('66');
  935.          writeln('MHz');
  936.        end;
  937.  
  938.       08 : begin
  939.              writeln('   HyperTransport Capability');
  940.              write('     SubType : ');
  941.              if infotbl[cap_ptr+3] and $e0=0 then writeln('Slave/Primary Interface') else
  942.              if infotbl[cap_ptr+3] and $e0=$20 then writeln('Host/Secondary Interface') else
  943.              case infotbl[cap_ptr+3] and $F8 of
  944.                $40 : writeln('Switch');
  945.                $80 : writeln('Interrupt Discovery & Configuration');
  946.                $88 : writeln('Revision ID');
  947.                $90 : writeln('UnitID Clumping');
  948.                $98 : writeln('Extended Configuration Space Access');
  949.                $A0 : writeln('Address Mapping');
  950.                $A8 : writeln('MSI Mapping');
  951.                $B0 : writeln('DirectRoute');
  952.                $B8 : writeln('VCSet');
  953.                $C0 : writeln('Retry mode');
  954.                $C8 : writeln('x86 Encoding(Reserved)');
  955.                else writeln('?? Unknown');
  956.              end;
  957.  
  958. { Slave/Pri : type 000xx}            
  959.              if infotbl[cap_ptr+3] and $e0=0 then
  960.              begin
  961.                writeln('     Base UnitID  : ',infotbl[cap_ptr+2] and $1f);
  962.                writeln('     UnitID Count : ',((infotbl[cap_ptr+3] and 3) shl 3) + (infotbl[cap_ptr+2] shr 5));
  963.              end;
  964.  
  965.            end;
  966.  
  967.  
  968.       09 : begin
  969.          writeln('   Vendor-Dependant Capability');
  970.        end;
  971.  
  972.      $0a : begin
  973.          writeln('   USB 2.0 EHCI Debug Port Capability');
  974.        end;
  975.  
  976.      $0b : begin
  977.          writeln('   CompactPCI Resource Control Capability');
  978.        end;
  979.  
  980.      $0c : begin
  981.          writeln('   PCI Hot-Plug Capability');
  982.        end;
  983.        
  984.      $0d : begin
  985.              writeln('   Subsystem ID & Subsystem Vendor ID Capability');
  986.              writeln('     SSVID : ',wrhexw(infotbl[cap_ptr + 5] shl 8 + infotbl[cap_ptr + 4]),'h');
  987.              writeln('     SSID  : ',wrhexw(infotbl[cap_ptr + 7] shl 8 + infotbl[cap_ptr + 6]),'h');
  988.            end;
  989.  
  990.      $0e : begin
  991.          writeln('   AGP 8x Capability');
  992.        end;
  993.  
  994.      $0f : begin
  995.          writeln('   Secure Device Capability');
  996.        end;
  997.  
  998.      $10 : begin
  999.          writeln('   PCI Express Capability, Version ',infotbl[cap_ptr+2] and $f);
  1000.  
  1001.  
  1002. { **** 3GIO Capabilities field 2-3}
  1003.          write('     Device/Port Type : ');
  1004.          case infotbl[cap_ptr+2] shr 4 of
  1005.            0 : writeln('PCI Express Endpoint Device');
  1006.            1 : writeln('Legacy PCI Express Endpoint Device');
  1007.            4 : writeln('Root port of PCI Express Root Complex');
  1008.            5 : writeln('Upstream port of PCI Express Switch');
  1009.            6 : writeln('Downstream port of PCI Express Switch');
  1010.            7 : writeln('PCI Express to PCI/PCI-X Bridge');
  1011.            9 : writeln('Root Complex Integrated Endpoint Device');
  1012.            else writeln('Unknown (',wrhex(infotbl[cap_ptr+2] shr 4),'h)!!');
  1013.           end;
  1014.  
  1015. { ports only }                
  1016.           if (infotbl[cap_ptr+2] shr 4=4) or (infotbl[cap_ptr+2] shr 4=6) then
  1017.           begin
  1018.             write('     Port is an ');
  1019.             if infotbl[cap_ptr+3] and 1=1 then writeln('Expansion Slot') else writeln('Integrated Device');
  1020.           end;
  1021.  
  1022. { **** Device Capabilities field 4-7}          
  1023. { devices only }          
  1024.           if (infotbl[cap_ptr+2] shr 4=0) or (infotbl[cap_ptr+2] shr 4=1) then
  1025.           begin
  1026.               if infotbl[cap_ptr+5] and 16=16 then writeln('     Attention Button Present on Device');
  1027.             if infotbl[cap_ptr+5] and 32=32 then writeln('     Attention Indicator Present on Device');
  1028.             if infotbl[cap_ptr+5] and 64=64 then writeln('     Power Indicator Present on Device');
  1029.           end;
  1030.  
  1031. { upstream ports only }
  1032.           if infotbl[cap_ptr+2] shr 4=5 then
  1033.           begin
  1034.               write('     Slot Power Limit Value : ');
  1035.         spl:=(infotbl[cap_ptr+6] shr 2) + ((infotbl[cap_ptr+7] and 3) shl 6);
  1036.               if ((infotbl[cap_ptr+7] and $c) shr 2)=0 then spl:=spl;
  1037.               if ((infotbl[cap_ptr+7] and $c) shr 2)=1 then spl:=spl*0.1;
  1038.               if ((infotbl[cap_ptr+7] and $c) shr 2)=2 then spl:=spl*0.01;
  1039.               if ((infotbl[cap_ptr+7] and $c) shr 2)=3 then spl:=spl*0.001;
  1040.               writeln(spl,' Watts');
  1041.             end;
  1042.             
  1043. { **** Device Control Field 8-9}
  1044.             if infotbl[cap_ptr+8] and 1=1 then writeln('     Correctable Error Reporting Enabled');
  1045.             if infotbl[cap_ptr+8] and 2=2 then writeln('     Non-Fatal Error Reporting Enabled');
  1046.             if infotbl[cap_ptr+8] and 4=4 then writeln('     Fatal Error Reporting Enabled');
  1047.             if infotbl[cap_ptr+8] and 8=8 then writeln('     Unsupported Request Reporting Enabled');
  1048.             write('     Unsupported Request Severity is ');
  1049.             if infotbl[cap_ptr+8] and 16=16 then writeln('Fatal') else writeln('Non-Fatal');
  1050.  
  1051.  
  1052. { **** Device Status field 0ah-0bh}
  1053.             if infotbl[cap_ptr+$a] and 1=1 then writeln('     Correctable Error Detected');
  1054.             if infotbl[cap_ptr+$a] and 2=2 then writeln('     Non-Fatal Error Detected');
  1055.             if infotbl[cap_ptr+$a] and 4=4 then writeln('     Fatal Error Detected');
  1056.             if infotbl[cap_ptr+$a] and 8=8 then writeln('     Unsupported Request Detected');
  1057.             if infotbl[cap_ptr+$a] and 16=16 then writeln('     AUX Power Detected');
  1058.             if infotbl[cap_ptr+$a] and 32=32 then writeln('     Device Reports Transactions Pending');
  1059.  
  1060.           
  1061. { **** Link Capabilities field 0ch-0fh}
  1062.           write('     Maximum Link speed : ');
  1063.           case infotbl[cap_ptr+$c] and $f of
  1064.             1 : writeln('2.5Gb/s');
  1065.             else writeln('Unknown (',wrhex(infotbl[cap_ptr+$c] and $f),'h)!!');
  1066.           end;
  1067.  
  1068.           write('     Maximum Link Width : x');
  1069.           lw:=((infotbl[cap_ptr+$d] and 3) shl 4) + ((infotbl[cap_ptr+$c] and $f0) shr 4);
  1070.           if lw=0 then writeln('Reserved') else writeln(lw);
  1071.  
  1072.           writeln('     Link Port Number   : ',infotbl[cap_ptr+$f]);
  1073.      
  1074. { **** Link Control 10h-11h }
  1075.  
  1076.             if infotbl[cap_ptr+$10] and 4=4 then writeln('     Link is in Loopback mode');
  1077.             if infotbl[cap_ptr+$10] and 16=16 then writeln('     Link is Disabled');
  1078.           if infotbl[cap_ptr+$10] and 64=64 then writeln('     Common Clock Configuration In Use')
  1079.         else writeln('Asynchronous Clocking in Use');
  1080.  
  1081.  
  1082. { **** Link Status 12h-13h } 
  1083.               write('     Current Link speed : ');
  1084.           case infotbl[cap_ptr+$12] and $f of
  1085.             1 : writeln('2.5Gb/s');
  1086.             else writeln('Unknown (',wrhex(infotbl[cap_ptr+$c] and $f),')!!');
  1087.           end;
  1088.           
  1089.           write('     Current Link Width : x');
  1090.           lw:=((infotbl[cap_ptr+$13] and 3) shl 4) + ((infotbl[cap_ptr+$12] and $f0) shr 4);
  1091.           if infotbl[cap_ptr+$13] and 8=8 then writeln('??') else writeln(lw);
  1092.           if infotbl[cap_ptr+$13] and 4=4 then writeln('     Link Training Error Reported!!');
  1093.           if infotbl[cap_ptr+$13] and 8=8 then writeln('     Link Training Currently In Progress!!');
  1094.  
  1095. { **** Slot Capabilities 14h-17h, slots & root ports only }
  1096.            if (infotbl[cap_ptr+2] shr 4)>1 then
  1097.            begin
  1098.              if infotbl[cap_ptr+$14] and 1=1 then writeln('     Attention Button Present');
  1099.              if infotbl[cap_ptr+$14] and 2=2 then writeln('     Power Controller Present');
  1100.              if infotbl[cap_ptr+$14] and 4=4 then writeln('     MRL Sensor Present');
  1101.              if infotbl[cap_ptr+$14] and 8=8 then writeln('     Attention Indicator Present');
  1102.              if infotbl[cap_ptr+$14] and 16=16 then writeln('     Power Indicator Present');
  1103.              if infotbl[cap_ptr+$14] and 32=32 then writeln('     Hot Plug Surprise is Possible');
  1104.              if infotbl[cap_ptr+$14] and 64=64 then writeln('     Hot Plug Capable');
  1105.            
  1106.              writeln('     Physical slot Number ',(word(word(infotbl[cap_ptr+$17]) shl 2)+(infotbl[cap_ptr+$16] shr 6)));
  1107.          end;
  1108.        end;
  1109.  
  1110.        
  1111.  
  1112.      $11 : begin
  1113.          writeln('   MSI-X Capability');
  1114.        end;
  1115.  
  1116.  
  1117.       else writeln('   Unknown Capability (Code ',wrhex(infotbl[cap_ptr]),'h)!!');
  1118.     end;
  1119.   cap_ptr:=infotbl[cap_ptr+1];
  1120.   until cap_ptr=0 else writeln('  No ''New Capabilities'' Are Currently Enabled!');
  1121. end;
  1122.  
  1123.  
  1124.  
  1125.  
  1126. procedure showallinfo;
  1127. var
  1128.   j,
  1129.   i     : integer;
  1130.   pp,
  1131.   nn,
  1132.   x    : byte;
  1133.   gotit    : boolean;
  1134.  
  1135. begin
  1136.     if businfo then
  1137.     begin
  1138.       write(' Bus ');
  1139.       textcolor(11);
  1140.       write(bus);
  1141.       textcolor(7);
  1142.       write(' (');
  1143.  
  1144. { look for CardBus }
  1145.       gotit:=false;
  1146.       if cardptr>0 then
  1147.       begin
  1148.         for x:=1 to cardptr do if bus=cardbus[x] then
  1149.         begin
  1150.           write('Cardbus');
  1151.           gotit:=true;
  1152.         end;
  1153.       end;
  1154.  
  1155.  
  1156. { look for PCI Express }
  1157. {type 0}  if infotbl[$e] and $7f=0 then cap_ptr:=infotbl[$34];
  1158. {type 1}  if infotbl[$e] and $7f=1 then cap_ptr:=infotbl[$34];
  1159. {type 2}  if infotbl[$e] and $7f=2 then cap_ptr:=infotbl[$14];
  1160.       if cap_ptr<>0 then
  1161.           repeat
  1162.             if infotbl[cap_ptr]=$10 then
  1163.             begin
  1164.               write('PCI Express');
  1165.               gotit:=true;
  1166.             end;
  1167.             cap_ptr:=infotbl[cap_ptr+1];
  1168.           until cap_ptr=0;
  1169.  
  1170.  
  1171. { we crudely assume bus x is AGP if it's got the VGA Mapping flag set on it's parent PCI bridge - this is probably
  1172.   not prefect, but will do for now!!
  1173.  
  1174.   It's wrong because on a board with an AGP slot but no AGP card inserted, VGA Mapping will be off. This could also
  1175.   be the case if the AGP card isn't the primary adapter (eg a PCI one is). This is more accurate than previous
  1176.   attempts, however, as it avoids the situation where a board with bridges but without AGP is falsely reported.
  1177.   
  1178.   Previously, we could get false positives (very bad); now we get false negitives (not so bad, but still not perfect) }
  1179.  
  1180.  
  1181.  
  1182. { try to guess AGP bus number by looking for a set VGA Mapping flag on a PCI-PCI Bridge. This works because the host
  1183.   bridge will always appear before it's own child busses... ie this 'detects' the bus, the next block actually reports
  1184.   the results in a later iteration (when the right bus number is finally reached) }
  1185.  
  1186.       if infotbl[$e] and $7f=1 then if infotbl[$3e] and 8=8 then agpbusnum:=infotbl[$19];
  1187.  
  1188. { assume plain old PCI if nothing else matches }
  1189.       if not gotit then if bus=agpbusnum then write('AGP') else write('PCI');
  1190.       write('), Device Number ');
  1191.           textcolor(11);
  1192.       write(deviceid);
  1193.           textcolor(7);
  1194.       write(', Device Function ');
  1195.           textcolor(11);
  1196.       writeln(func);
  1197.           textcolor(7);
  1198.     end;
  1199.  
  1200.  
  1201.       if installermode then showinstallerinfo else
  1202.       begin
  1203.  
  1204.  
  1205.  
  1206.  
  1207.       write(' Vendor ',wrhexw(infotbl[1] shl 8+infotbl[0]),'h ');
  1208.       cmpstr:=wrhexw(infotbl[1] shl 8+infotbl[0]);
  1209.       lookupven(false);
  1210.       writeln;
  1211.  
  1212.  
  1213.       write(' Device ',wrhexw(infotbl[3] shl 8+infotbl[2]),'h ');
  1214.       cmpstr:=wrhexw(infotbl[3] shl 8+infotbl[2]);
  1215.       lookupdev;
  1216.       writeln;
  1217.  
  1218.  
  1219.       if not summary then
  1220.       begin
  1221.         write(' Command ',wrhexw(infotbl[5] shl 8+infotbl[4]),'h');
  1222.         first:=true;
  1223.         write(' (');
  1224.         if infotbl[4] and 1=1 then printstatus('I/O Access');
  1225.         if infotbl[4] and 2=2 then printstatus('Memory Access');
  1226.         if infotbl[4] and 3=0 then printstatus('Bus Access Disabled!!');
  1227.         if infotbl[4] and 4=4 then printstatus('BusMaster');
  1228.         if infotbl[4] and 8=8 then printstatus('Special Cycles');
  1229.         if infotbl[4] and 16=16 then printstatus('MemWrite+Invalidate');
  1230.         if infotbl[4] and 32=32 then printstatus('VGA Palette Snoop');
  1231.         if infotbl[4] and 64=64 then printstatus('Parity Error Response');
  1232.         if infotbl[4] and 128=128 then printstatus('Wait Cycles');
  1233.         if infotbl[5] and 1=1 then printstatus('System Errors');
  1234.         if infotbl[5] and 2=2 then printstatus('Back-To-Back Transactions');
  1235.         if infotbl[5] and 4=4 then printstatus('Interrupt Disable');
  1236.         writeln(')');
  1237.  
  1238.  
  1239.         write(' Status ',wrhexw(infotbl[7] shl 8+infotbl[6]),'h');
  1240.         if (infotbl[6]<>0) or (infotbl[7]<>0) then
  1241.         begin
  1242.           first:=true;
  1243.           write(' (');
  1244.           if infotbl[6] and 8=8 then printstatus('Signalled Interrupt');
  1245.           if infotbl[6] and 16=16 then printstatus('Has Capabilities List');
  1246.           if infotbl[6] and 32=32 then printstatus('Supports 66MHz');
  1247.           if infotbl[6] and 64=64 then printstatus('Has UDF');
  1248.           if infotbl[6] and 128=128 then printstatus('Supports Back-To-Back Trans.');
  1249.  
  1250.           if infotbl[7] and 1=1 then printstatus('Data parity Error Detected');
  1251.           if infotbl[7] and 8=8 then printstatus('Signalled Target Abort');
  1252.           if infotbl[7] and 16=16 then printstatus('Received Target Abort');
  1253.           if infotbl[7] and 32=32 then printstatus('Received Master Abort');
  1254.           if infotbl[7] and 64=64 then printstatus('Signalled System Error');
  1255.           if infotbl[7] and 128=128 then printstatus('Detected Parity Error');
  1256.  
  1257.           case ((infotbl[7] and 6) shr 1) of
  1258.         0 : printstatus('Fast Timing');
  1259.         1 : printstatus('Medium Timing');
  1260.         2 : printstatus('Slow Timing');
  1261.         3 : printstatus('Unknown Timing');
  1262.           end;
  1263.           write(')');
  1264.  
  1265.         end;
  1266.         writeln;
  1267.  
  1268.         write(' Revision ',wrhex(infotbl[8]),'h');
  1269.         write(', Header Type ',wrhex(infotbl[$e]),'h');
  1270.         writeln(', Bus Latency Timer ',wrhex(infotbl[$d]),'h');
  1271.  
  1272. { header type 0 only : display latency and grant figures }
  1273.        if infotbl[$e] and $7f=0 then
  1274.        begin
  1275.          if (infotbl[$3e]<>0) or (infotbl[$3f]<>0) then
  1276.            writeln(' Minimum Bus Grant ',wrhex(infotbl[$3e]),'h, Maximum Bus Latency ',wrhex(infotbl[$3f]),'h');
  1277.        end;
  1278.  
  1279. { self test }
  1280.         write(' Self test ',wrhex(infotbl[$f]),'h (Self test ');
  1281.         if infotbl[$f] and $80=0 then write('not ');
  1282.         write('supported');
  1283.  
  1284.  
  1285.         if infotbl[$f] and $80=$80 then
  1286.         begin
  1287.           write(': Completion code ',wrhexb(infotbl[$f] and $f),'h - ');
  1288.           if infotbl[$f] and $f=0 then
  1289.           begin
  1290.         textcolor(10);
  1291.         write('OK');
  1292.         textcolor(7);
  1293.           end else
  1294.           begin
  1295.         textcolor(12);
  1296.         write('Failed!!');
  1297.         textcolor(7);
  1298.           end;
  1299.         end;
  1300.  
  1301.         writeln(')');
  1302.  
  1303.  
  1304.         if infotbl[$c]<>0 then writeln(' Cache line size ',infotbl[$c]*4,' Bytes (',infotbl[$c],' DWords)');
  1305.  
  1306. { class code stuff }
  1307.  
  1308.         write(' PCI Class ');
  1309.  
  1310.         if infotbl[$b]=$ff then
  1311.         begin
  1312.           write('FFh ');
  1313.           textcolor(10);
  1314.           write('(does not meet any PCI-SIG defined class)');
  1315.           textcolor(7);
  1316.         end else
  1317.         begin
  1318.         
  1319.         found:=false;
  1320.  
  1321.  
  1322.         for i:=0 to high_class_name do
  1323.         if infotbl[$b]=i then
  1324.           begin
  1325.           textcolor(14);
  1326.           write(PCI_class_names[i]);
  1327.           textcolor(7);
  1328.           found:=true;
  1329.         end;
  1330.         
  1331.         if not found then
  1332.         begin
  1333.           textcolor(12);
  1334.           write('Unknown! ');
  1335.           textcolor(7);
  1336.           write('(Class ',wrhex(infotbl[$b]),'h)')
  1337.         end;
  1338.  
  1339.         write(', type ');
  1340.  
  1341.  
  1342.         found:=false;
  1343.  
  1344.         for i:=0 to high_class_array do
  1345.          begin
  1346.           if (pci_class_array[i].class=infotbl[$b]) and
  1347.           (pci_class_array[i].subclass=infotbl[$a]) and
  1348.           (pci_class_array[i].progif=infotbl[$9]) then
  1349.           begin
  1350.         found:=true;
  1351.         textcolor(14);
  1352.         write(PCI_class_array[i].name);
  1353.         textcolor(7);
  1354.           end;
  1355.         end;
  1356.  
  1357.  
  1358.         if not found then
  1359.         begin
  1360.           for i:=0 to high_class_array do
  1361.           begin
  1362.         if (pci_class_array[i].class=infotbl[$b]) and
  1363.         (pci_class_array[i].subclass=infotbl[$a]) then
  1364.         begin
  1365.           found:=true;
  1366.           textcolor(14);
  1367.           write(PCI_class_array[i].name);
  1368.           textcolor(7);
  1369.         end;
  1370.           end;
  1371.         end;
  1372.  
  1373.  
  1374.         if not found then
  1375.         begin
  1376.           textcolor(12);
  1377.           write('Unknown!');
  1378.           textcolor(7);
  1379.                write(' (Subclass ',wrhex(infotbl[$a]),'h, ProgIF ',wrhex(infotbl[9]),'h)');
  1380.         end;
  1381.         end;
  1382.         writeln;
  1383.       end;
  1384.  
  1385.  
  1386.  
  1387.  
  1388.  
  1389.  
  1390.       if not summary then
  1391.       begin
  1392. { look for generic PCI IDE controller & decode it's info, if present }
  1393.        if (infotbl[$b]=01) and (infotbl[$a]=01) then
  1394.        begin
  1395.          writeln(' PCI EIDE Controller Features :');
  1396.          write('   BusMaster EIDE is ');
  1397.          if infotbl[$9] and $80=0 then
  1398.          begin
  1399.            textcolor(12);
  1400.            write('NOT ');
  1401.            textcolor(7);
  1402.          end;
  1403.          writeln('supported');
  1404.  
  1405.          write('   Primary   Channel is ');
  1406.          if infotbl[$9] and 1=0 then
  1407.          begin
  1408.            writeln('at I/O Port 01F0h and IRQ 14');
  1409.            if infotbl[$3c]<>14 then inc(irqmap[14]);
  1410.          end else writeln('in native mode at Addresses 0 & 1');
  1411.          write('   Secondary Channel is ');
  1412.          if infotbl[$9] and 4=0 then
  1413.          begin
  1414.            writeln('at I/O Port 0170h and IRQ 15');
  1415.            if infotbl[$3c]<>15 then inc(irqmap[15]);
  1416.          end else writeln('in native mode at Addresses 2 & 3');
  1417.        end;
  1418.  
  1419.        end else
  1420.        begin
  1421. { summary mode: pick up IRQs only }
  1422.          if (infotbl[$b]=01) and (infotbl[$a]=01) then
  1423.          begin
  1424.            if (infotbl[$9] and 1=0) and (infotbl[$3c]<>14) then inc(irqmap[14]);
  1425.            if (infotbl[$9] and 4=0) and (infotbl[$3c]<>15) then inc(irqmap[15]);
  1426.          end;
  1427.        end;
  1428.  
  1429.  
  1430.  
  1431.  
  1432. { if type 0 table & if Subsystem ID exists, display and scan file for match }
  1433.        if infotbl[$e] and $7f=0 then
  1434.        if (infotbl[$2c]<>0) or (infotbl[$2d]<>0) or (infotbl[$2e]<>0) or (infotbl[$2f]<>0) then
  1435.        begin
  1436.  
  1437. { subsystem ID }
  1438.  
  1439.  
  1440.          write(' Subsystem ID ',wrhexw(infotbl[$2f] shl 8+infotbl[$2e]));
  1441.          write(wrhexw(infotbl[$2d] shl 8+infotbl[$2c]),'h');
  1442.          cmpstr:=wrhexw(infotbl[$2f] shl 8+infotbl[$2e])+wrhexw(infotbl[$2d] shl 8+infotbl[$2c]);
  1443.  
  1444.  
  1445.          genssid:=false;
  1446.          if (infotbl[$2c]=infotbl[0])
  1447.          and (infotbl[$2d]=infotbl[1])
  1448.          and (infotbl[$2e]=infotbl[2])
  1449.          and (infotbl[$2f]=infotbl[3]) then genssid:=true;
  1450.  
  1451.          oemidnum:='';
  1452.          oemidstr:='';
  1453.          bogusid:=false;
  1454.          failed:=true;
  1455. { use the line that was read for revchk, or the first O or X entry will be missed! }
  1456.          userev:=true;
  1457.  
  1458.  
  1459.          if not eof(f) then
  1460.          begin
  1461.            repeat
  1462.          if userev and (revchk<>'') then vstr:=revchk else readln(f,vstr);
  1463.          userev:=false;
  1464.  
  1465. { OEM Vendor ID }
  1466.          if vstr[1]='O' then
  1467.          begin
  1468.            if copy(vstr,3,4)=copy(cmpstr,5,4) then
  1469.            begin
  1470.              oemidstr:=copy(vstr,8,length(vstr)); { closest match }
  1471.              oemidnum:=copy(vstr,3,4); { matching vendor name }
  1472.            end;
  1473.          end;
  1474.  
  1475.  
  1476.          if vstr[1]='S' then
  1477.          begin
  1478.            if copy(vstr,3,4)=copy(cmpstr,1,4) then
  1479.            begin
  1480.              if oemidnum<>'' then
  1481.              begin
  1482.                oemidstr:=copy(vstr,8,length(vstr));
  1483.                begin
  1484.              textcolor(14);
  1485.              write(' ',oemidstr);
  1486.              if genssid then
  1487.              begin
  1488.                textcolor(11);
  1489.                writeln(' (Generic ID)')
  1490.              end else writeln;
  1491.              failed:=false;
  1492.              textcolor(7);
  1493.                end;
  1494.              end;
  1495.            end;
  1496.          end;
  1497.  
  1498.  
  1499.  
  1500.  
  1501.  
  1502.  
  1503.  
  1504. { Oddball 8 digit entry }
  1505.          if (vstr[1]='X') and (copy(vstr,3,8)=cmpstr) then
  1506.          begin
  1507.            oemidnum:=copy(vstr,7,4); { matching vendor name }
  1508.            bogusid:=true;
  1509.            textcolor(14);
  1510.            write(' ',copy(vstr,12,length(vstr)));
  1511.            if genssid then
  1512.            begin
  1513.              textcolor(11);
  1514.              writeln(' (Generic ID)')
  1515.            end else writeln;
  1516.            failed:=false;
  1517.            textcolor(7);
  1518.          end;
  1519.  
  1520.  
  1521. { remember to ignore comment lines here also!!! }
  1522.  
  1523.            until eof(f) or not failed or ((vstr[1]<>'O') and (vstr[1]<>'X') and (vstr[1]<>'S') and(vstr[1]<>';'));
  1524.          end;
  1525.  
  1526.  
  1527.  
  1528.  
  1529.  
  1530.  
  1531.          if failed then
  1532.          begin
  1533.            if oemidstr<>'' then
  1534.            begin
  1535.          textcolor(14);
  1536.          write(' ',oemidstr);
  1537.          textcolor(15);
  1538.          write(' (Guess Only!)');
  1539.          textcolor(7);
  1540.            end else
  1541.            begin
  1542.          textcolor(12);
  1543.          write(' Unknown');
  1544.            end;
  1545.  
  1546.            if genssid then
  1547.            begin
  1548.          textcolor(11);
  1549.          writeln(' (Generic ID)')
  1550.            end else writeln;
  1551.            textcolor(7);
  1552.          end;
  1553.  
  1554.  
  1555. { subsystem vendor }
  1556.          write(' Subsystem Vendor ',wrhexw(infotbl[$2d] shl 8+infotbl[$2c]),'h');
  1557.  
  1558.          if bogusid then
  1559.          begin
  1560.            textcolor(15);
  1561.            writeln(' Known Bad Subsystem ID - no Vendor ID Available');
  1562.            textcolor(7);
  1563.          end else
  1564.          begin
  1565.            if oemidnum<>'' then cmpstr:=oemidnum
  1566.            else cmpstr:=wrhexw(infotbl[$2d] shl 8+infotbl[$2c]);
  1567.            close(f);        { get back to start of file, as the}
  1568.            reset(f);        { subsys vendor may be higher up...!}
  1569.            failed:=true;
  1570.            if not eof(f) then
  1571.            begin
  1572.          repeat
  1573.            readln(f,vstr);
  1574.                    if length(vstr)<3 then vstr:=';   oops';
  1575.            if (vstr[1]='V') and (copy(vstr,3,4)=cmpstr) then
  1576.            begin
  1577.              textcolor(14);
  1578.              writeln(' ',copy(vstr,8,length(vstr)));
  1579.              failed:=false;
  1580.              textcolor(7);
  1581.            end;
  1582.          until eof(f) or not failed;
  1583.            end;
  1584.            if failed then
  1585.            begin
  1586.          textcolor(12);
  1587.          writeln(' Unknown');
  1588.          textcolor(7);
  1589.            end;
  1590.          end;
  1591.        end;
  1592. { always }
  1593.        close(f);
  1594.  
  1595.  
  1596.  
  1597.  
  1598. { Memory & I/O  registers }
  1599.        if not summary then
  1600.        begin
  1601. { type 0 header = 6 entries, type 1 = 2, type 2 = skip }
  1602.        pp:=0;
  1603.        if infotbl[$e] and $7f=0 then pp:=5;
  1604.        if infotbl[$e] and $7f=1 then pp:=1;
  1605.  
  1606.        if pp>0 then for nn:=0 to pp do
  1607.        begin
  1608.          if infotbl[$10+(nn*4)]+infotbl[$11+(nn*4)]+
  1609.            infotbl[$12+(nn*4)]+infotbl[$13+(nn*4)]<>0 then
  1610.          begin
  1611.            write(' Address ',nn,' is a');
  1612.            if infotbl[$10+(nn*4)] and 1=1 then
  1613.            begin
  1614.          write('n I/O Port : ');
  1615.          addr:=infotbl[$13+(nn*4)] shl 8 + infotbl[$12+(nn*4)];
  1616.          write(wrhexw(addr));
  1617.          addr:=infotbl[$11+(nn*4)] shl 8 + (infotbl[$10+(nn*4)] and $fc);
  1618.          write(wrhexw(addr),'h');
  1619.            end else
  1620.            begin
  1621.          write(' Memory Address');
  1622.          if infotbl[$10+(nn*4)] and 6=0 then write(' (anywhere in 0-4Gb');
  1623.          if infotbl[$10+(nn*4)] and 6=2 then write(' (below 1Mb');
  1624.          if infotbl[$10+(nn*4)] and 6=4 then write(' (anywhere in 64-bit space');
  1625.          if infotbl[$10+(nn*4)] and 6=6 then write(' (reserved');
  1626.          if infotbl[$10+(nn*4)] and 8=8 then write(', Prefetchable) : ') else write(') : ');
  1627.          addr:=infotbl[$13+(nn*4)] shl 8 + infotbl[$12+(nn*4)];
  1628.          write(wrhexw(addr));
  1629.          addr:=infotbl[$11+(nn*4)] shl 8 + (infotbl[$10+(nn*4)] and $f0);
  1630.          write(wrhexw(addr)+'h');
  1631.            end;
  1632.  
  1633. { size the register ?? }
  1634.  
  1635.            writeln;
  1636.          end;
  1637.        end;
  1638.  
  1639.  
  1640.        end;
  1641.  
  1642.  
  1643. { all header types - list IRQ, if present }
  1644.        if (infotbl[$3c]<16) and (infotbl[$3c]>0) then
  1645.        begin
  1646.          write(' System IRQ ',infotbl[$3c],', INT# ');
  1647.          if infotbl[$3d]=0 then write('-') else write(chr(infotbl[$3d]+64));
  1648.          writeln;
  1649.          inc(irqmap[infotbl[$3c]]);
  1650.        end;
  1651.  
  1652.  
  1653.  
  1654.  
  1655. { type 0,1 header only - List ExpROM, if present }
  1656.        if not summary then
  1657.        begin
  1658.        if (infotbl[$e] and $7f=0) or (infotbl[$e] and $7f=1) then
  1659.        begin
  1660.          if infotbl[$e] and $7f=0 then lb:=$30;
  1661.          if infotbl[$e] and $7f=1 then lb:=$38;
  1662.  
  1663. { backup original value }
  1664.                rom_backup:=infotbl[lb+3] shl 24 + infotbl[lb+2] shl 16 + infotbl[lb+1] shl 8 + infotbl[lb];
  1665. { perform test write }
  1666.          if usebios then write_dword_bios(deviceid,func,bus,lb,$ffff,$fffe) else write_dword_hw(deviceid,func,bus,lb,$ffff,$fffe);
  1667.          for i:=lb to lb+3 do if usebios then infotbl[i]:=lookup_bios(deviceid,func,bus,i)
  1668.            else infotbl[i]:=lookup_hw(deviceid,func,bus,i);
  1669. { restore original value regardless }
  1670.            if usebios then write_dword_bios(deviceid,func,bus,lb,rom_backup shr 16,rom_backup and $ffff)
  1671.          else write_dword_hw(deviceid,func,bus,lb,rom_backup shr 16,rom_backup and $ffff);
  1672. { if any bit in 31..11 = 1, we have expROM }
  1673.            if (infotbl[lb+3]<>0) or (infotbl[lb+2]<>0) or ((infotbl[lb+1] and $f8)<>0) then
  1674.            begin
  1675. { find lowest 1-bit, gives expROM size }
  1676.          romsize:=1;
  1677.              romresult:=infotbl[lb+3] shl 24 + infotbl[lb+2] shl 16 + ((infotbl[lb+1] and $f8) shl 8);
  1678.          found:=false;
  1679.          repeat
  1680.            if romresult and romsize=romsize then found:=true else romsize:=romsize shl 1;
  1681.          until found or (romsize=0);
  1682.          romsize:=romsize shr 10;
  1683.          write(' Expansion ROM of ');
  1684.              if romsize>1023 then write(romsize shr 10,'Mb') else write(romsize,'Kb');
  1685.              write(' decoded by this card (Currently ');
  1686.              if rom_backup and 1=1 then writeln('enabled)') else writeln('disabled)');
  1687.            end;
  1688. { restore infotbl[] to original values }
  1689.            infotbl[lb]:=rom_backup and $ff;
  1690.            infotbl[lb+1]:=(rom_backup shr 8) and $ff;
  1691.            infotbl[lb+2]:=(rom_backup shr 16) and $ff;
  1692.            infotbl[lb+3]:=(rom_backup shr 24) and $ff;
  1693.          end;
  1694.        end;
  1695.  
  1696.  
  1697.  
  1698.  
  1699.  
  1700.  
  1701.  
  1702.  
  1703.  
  1704. { PCI Bridges info starts here }
  1705.  
  1706. { type 1 header only - List bridge info }
  1707.  
  1708.        if not summary then
  1709.        begin
  1710.  
  1711.        if infotbl[$e] and $7f=1 then
  1712.        begin
  1713.           writeln(' PCI Bridge Information:');
  1714.          write('   Primary Bus Number ',infotbl[$18],', Secondary Bus Number ',infotbl[$19]);
  1715.          writeln(', Subordinate Bus Number ',infotbl[$1a]);
  1716.  
  1717. { seconday bus command }
  1718.          first:=true;
  1719.          write('   Secondary Bus Command ',wrhexw(infotbl[$3f] shl 8 + infotbl[$3e]),'h ');
  1720.          write('(');
  1721.          if infotbl[$3e] and 1=1 then printstatus('parity detection');
  1722.          if infotbl[$3e] and 4=4 then printstatus('ISA mapping');
  1723.          if infotbl[$3e] and 8=8 then printstatus('VGA mapping');
  1724.          if infotbl[$3e] and 32=32 then printstatus('master abort mode');
  1725.          if infotbl[$3e] and 64=64 then printstatus('secondary bus is in RESET');
  1726.          if infotbl[$3e] and 128=128 then printstatus('back-to-back transactions');
  1727.          writeln(')');
  1728.  
  1729.  
  1730. { secondary bus status }
  1731.         write('   Secondary Bus Status ',wrhexw(infotbl[$1f] shl 8+infotbl[$1e]),'h');
  1732.         if (infotbl[$1e]<>0) or (infotbl[$1f]<>0) then
  1733.         begin
  1734.           first:=true;
  1735.           write(' (');
  1736.           if infotbl[$1e] and 32=32 then printstatus('Supports 66MHz');
  1737.           if infotbl[$1e] and 128=128 then printstatus('Supports Back-To-Back Trans.');
  1738.           if infotbl[$1f] and 1=1 then printstatus('Data parity Error Detected');
  1739.           if infotbl[$1f] and 8=8 then printstatus('Signalled Target Abort');
  1740.           if infotbl[$1f] and 16=16 then printstatus('Received Target Abort');
  1741.           if infotbl[$1f] and 32=32 then printstatus('Received Master Abort');
  1742.           if infotbl[$1f] and 64=64 then printstatus('Received System Error');
  1743.           if infotbl[$1f] and 128=128 then printstatus('Detected Parity Error');
  1744.           case ((infotbl[$1f] and 6) shr 1) of
  1745.         0 : printstatus('Fast Timing');
  1746.         1 : printstatus('Medium Timing');
  1747.         2 : printstatus('Slow Timing');
  1748.         3 : printstatus('Unknown Timing');
  1749.           end;
  1750.           write(')');
  1751.         end;
  1752.         writeln;
  1753.  
  1754. { latency }
  1755.         writeln('   Secondary Bus Latency ',wrhex(infotbl[$1b]),'h');
  1756.  
  1757. { I/O port range passed by bridge }
  1758.          if (infotbl[$1c]<>0) or (infotbl[$1d]<>0) then
  1759.          begin
  1760.            write('   I/O Port Range Passed to Secondary Bus : ');
  1761.  
  1762. { 16-bit I/O }
  1763.            if infotbl[$1c] and $f=0 then
  1764.            begin
  1765.          if infotbl[$1d]<infotbl[$1c] then writeln('None') else
  1766.          begin
  1767.            write(wrhexb(infotbl[$1c] shr 4),'000h to ');
  1768.            writeln(wrhexb(infotbl[$1d] shr 4),'FFFh (16-bit)');
  1769.          end;
  1770.            end;
  1771. { 32-bit I/O }
  1772.            if infotbl[$1c] and $f=1 then
  1773.            begin
  1774.          if (infotbl[$33] shl 8 + infotbl[$32]) < (infotbl[$31] shl 8 + infotbl[$30]) then writeln('None') else
  1775.          begin
  1776.            write(wrhexw(infotbl[$31] shl 8 + infotbl[$30]),wrhexb(infotbl[$1c] shr 4),'000h to ');
  1777.            writeln(wrhexw(infotbl[$33] shl 8 + infotbl[$32]),wrhexb(infotbl[$1d] shr 4),'FFFh (32-bit)');
  1778.          end;
  1779.            end;
  1780.          end;
  1781.  
  1782.  
  1783.  
  1784. { memory range passed by bridge }
  1785.          write('   Memory   Range Passed to Secondary Bus : ');
  1786.          mb:=((infotbl[$21] shl 8) + infotbl[$20]) and $fff0;
  1787.          ml:=((infotbl[$23] shl 8) + infotbl[$22]) or $000f;
  1788.          if ml<mb then writeln('None') else writeln(wrhexw(mb),'0000h to ',wrhexw(ml),'FFFFh');
  1789.  
  1790.  
  1791. { optional: prefectchable memory range passed by bridge }
  1792.          if ((infotbl[$27] shl 8 + infotbl[$26])<>0) or ((infotbl[$25] shl 8 + infotbl[$24])<>0) then
  1793.          begin
  1794.            write('   Prefectchable Memory Range Passed to Secondary Bus : ');
  1795.            mb:=((infotbl[$25] shl 8) + infotbl[$24]) and $fff0;
  1796.            ml:=((infotbl[$27] shl 8) + infotbl[$26]) or $000f;
  1797.                if ml<mb then writeln('None') else
  1798.                begin
  1799. { 64 bit base reg? }               
  1800.          if infotbl[$24] and $0f=1 then
  1801.            write(wrhexw(infotbl[$2b] shl 8 + infotbl[$2a]),wrhexw(infotbl[$29] shl 8 + infotbl[$28]));
  1802.              write(wrhexw(mb),'0000h to ');
  1803. { 64 bits limit reg? }           
  1804.          if infotbl[$26] and $0f=1 then
  1805.            write(wrhexw(infotbl[$2f] shl 8 + infotbl[$2e]),wrhexw(infotbl[$2d] shl 8 + infotbl[$2c]));
  1806.              writeln(wrhexw(ml),'FFFFh');
  1807.            end;
  1808.          end;
  1809.  
  1810.  
  1811.        end;
  1812.        end;
  1813.  
  1814.  
  1815.  
  1816.  
  1817.  
  1818. { type 2 header only - List bus numbers etc }
  1819.  
  1820.        if not summary then
  1821.        begin
  1822.          if infotbl[$e] and $7f=2 then
  1823.          begin
  1824.            write(' PCI bus number ',infotbl[$18],', CardBus bus number ',infotbl[$19]);
  1825.            writeln(', Subordinate bus number ',infotbl[$1a]);
  1826.            writeln(' CardBus latency ',wrhex(infotbl[$1b]),'h');
  1827.          end;
  1828.        end;
  1829.  
  1830.  
  1831. { explore the capabilities list, if present }
  1832.        if not summary then
  1833.        begin
  1834.          if (infotbl[6] and $10=$10) then docapdecode;
  1835.        end;
  1836.  
  1837.  
  1838. { do a hex-dump, if requested }
  1839.        if dumpregs then
  1840.        begin
  1841.          writeln;
  1842.          writeln(' Hex-Dump of device configuration space follows:');
  1843.          write('  0000  ');
  1844.          for i:=0 to $ff do
  1845.          begin
  1846.            if (i>0) and (i mod 16=0) then
  1847.            begin
  1848.          write('   ');
  1849.          for j:=i-16 to i-1 do if ord(infotbl[j])<32 then write('.') else write(chr(infotbl[j]));
  1850.          writeln;
  1851.          write('  ',wrhexw(i),'  ');
  1852.            end;
  1853.            write(wrhex(infotbl[i]),' ');
  1854.          end;
  1855.          write('   ');
  1856.          for j:=240 to 255 do if ord(infotbl[j])<32 then write('.') else write(chr(infotbl[j]));
  1857.          writeln;
  1858.        end;
  1859.  
  1860.     writeln;
  1861.   end;
  1862. end;
  1863.  
  1864.  
  1865.  
  1866.  
  1867.  
  1868.  
  1869.  
  1870.  
  1871.  
  1872.  
  1873.  
  1874.  
  1875.  
  1876.  
  1877.  
  1878.  
  1879. begin
  1880.   agpbusnum:=$ff;
  1881.   businfo:=false;
  1882.   dorouting:=true;
  1883.   dopcirouting:=false;
  1884.   dumpregs:=false;
  1885.   usebios:=true;
  1886.   summary:=false;
  1887.   installermode:=false;
  1888.   cardptr:=0;
  1889.  
  1890. { the following hack permits MS-DOS display output redirection to work }
  1891.   if ioredirected then
  1892.   begin
  1893.     writeln('Craig Hart''s PCI+AGP bus sniffer, version ',revision,', freeware made in 1996-2001.');
  1894.     assign(output,'');
  1895.     rewrite(output);
  1896.   end else
  1897. { code to do page pausing }
  1898.   begin
  1899.     ClrScr;
  1900.     linecounter:=0;
  1901.     with TextRec(Output) do
  1902.     begin
  1903.       org_output:=FlushFunc;
  1904.       FlushFunc:=@page_output_FlushFunc;
  1905.     end;
  1906.   end;
  1907.  
  1908.  
  1909.  
  1910.   for i:=0 to 15 do irqmap[i]:=0;
  1911.   failed:=true;
  1912.  
  1913.  
  1914.  
  1915.  
  1916.   if paramcount>0 then
  1917.   begin
  1918.     for i:=1 to paramcount do
  1919.     begin
  1920.       cmdstr:=paramstr(i);
  1921.       for j:=1 to length(cmdstr) do cmdstr[j]:=upcase(cmdstr[j]);
  1922.       if (cmdstr='/R') or (cmdstr='-R') then showtree:=true;
  1923.       if (cmdstr='/H') or (cmdstr='-H') then usebios:=false;
  1924.       if (cmdstr='/D') or (cmdstr='-D') then dumpregs:=true;
  1925.       if (cmdstr='/T') or (cmdstr='-T') then dorouting:=false;
  1926.       if (cmdstr='/P') or (cmdstr='-P') then dopcirouting:=true;
  1927.       if (cmdstr='/B') or (cmdstr='-B') then businfo:=true;
  1928.       if (cmdstr='/S') or (cmdstr='-S') then summary:=true;
  1929.       if (cmdstr='/I') or (cmdstr='-I') then installermode:=true;
  1930.       if (cmdstr='?') or (cmdstr='/?') or (cmdstr='-?') then
  1931.       begin
  1932.     textmode(co80);
  1933.     writeln(' Help for PCI  (Version ',revision,')');
  1934.     textcolor(8);
  1935.     writeln('───────────────────────────────');
  1936.     textcolor(7);
  1937.     writeln;
  1938.     writeln('Usage: PCI [-H][-D][-S][-T][-B][-P][-R][-?]    [ ] indicates optional parameter');
  1939.     writeln;
  1940.     writeln('-H : Use direct hardware access (instead of the BIOS) to retrieve PCI Info');
  1941.     writeln('     May be required for accurate reporting on Intel 430FX chipset+Award BIOS');
  1942.     writeln('-D : Do a hex-dump of each device''s entire configuration space');
  1943.     writeln('-S : Create a brief, summary report only; only devices and IRQs listed');
  1944.     writeln('-T : Disable the test ROM IRQ Routing Table function');
  1945.     writeln('-B : Enable display of the Bus, Device & Function information');
  1946.     writeln('-P : Enable display of PCI slot routing data');
  1947.     writeln('-I : Installer mode: produce raw data dump (for use with auto-setup programs)');
  1948.     writeln('-R : Draw a Tree of Busses, Devices and Device Functions');
  1949.     writeln('-? : Displays this help screen!');
  1950.     writeln;
  1951.     writeln('PCI Supports generating reports to a file or printer using MS-DOS pipes; i.e.');
  1952.     writeln;
  1953.     writeln('  PCI -D > REPORT.TXT  (Save report to file),  PCI > LPT1:  (Print report)');
  1954.     writeln;
  1955.     writeln('PCI is written by Craig Hart, and is released as freeware, with no restictions');
  1956.     write('on use or copying. Visit ');
  1957.     textcolor(11);
  1958.     write('http://members.datafast.net.au/dft0802 ');
  1959.     textcolor(7);
  1960.     writeln('for updates to');
  1961.     writeln('the program and the PCI Database file PCIDEVS.TXT');
  1962.     halt(10);
  1963.       end;
  1964.     end;
  1965.   end;
  1966.  
  1967.  
  1968. { fix up conflicting commandline switches }
  1969.  
  1970.   if installermode then
  1971.   begin
  1972.     dorouting:=false;
  1973.     dopcirouting:=false;
  1974.     dumpregs:=false;
  1975.     businfo:=false;
  1976.     summary:=false;
  1977.   end;
  1978.  
  1979.   if summary then
  1980.   begin
  1981.     dumpregs:=false;
  1982.     dopcirouting:=false;
  1983.     dorouting:=false;
  1984.   end;
  1985.  
  1986.  
  1987.  
  1988.  
  1989.   if not installermode then
  1990.   begin
  1991.     assign(f,'pcidevs.txt');
  1992.     {$i-}
  1993.     reset(f);
  1994.     if ioresult<>0 then
  1995.     begin
  1996.       writeln('PCI Halted:');
  1997.       writeln;
  1998.       writeln('Sorry, I cannot locate my PCIDEVS.TXT datafile!!!');
  1999.       writeln('I expect it to be in the CURRENT directory, so don''t run me from a path!!!');
  2000.       halt(10);
  2001.     end;
  2002.     close(f);
  2003.     {$i+}
  2004.   end;
  2005.  
  2006.  
  2007.  
  2008.   if test8086<2 then
  2009.   begin
  2010.     writeln('PCI Halted:');
  2011.     writeln;
  2012.     writeln('The PC Must be at least a 386 to possibly have a PCI or AGP bus!');
  2013.     halt(1);
  2014.   end;
  2015.  
  2016. { Look for PCI BIOS }
  2017.  
  2018.   asm
  2019.     mov ax,$b101
  2020.     int $1a
  2021.     jc @exit
  2022.  
  2023. { check signature bytes OK }
  2024.     cmp dx,$4350
  2025.     jne @exit
  2026.  
  2027. { check no error code returned > AH=00=Success }
  2028.     cmp ah,0
  2029.     jne @exit
  2030.  
  2031.     mov PCIchar,al
  2032.     mov PCI_hibus,cl
  2033.     mov PCIverlo,bl
  2034.     mov PCIverhi,bh
  2035.     mov failed,false
  2036.  
  2037.   @exit:
  2038.   end;
  2039.  
  2040.   if failed then
  2041.   begin
  2042.     writeln('PCI Halted:');
  2043.     writeln;
  2044.     writeln('No PCI BIOS was detected! (NB: I don''t work under Windows NT/2000/XP/2003 etc!)');
  2045.     writeln;
  2046.     writeln('For PCI reports under Win NT-era OS''s such as NT/2K/XP/2K3, use PCI32.EXE');
  2047.     writeln('which is available from the same website as this program (See PCI /? for the');
  2048.     writeln('website address.)');
  2049.     writeln;
  2050.     halt(2);
  2051.   end;
  2052.  
  2053.  
  2054.  
  2055. { OK, we have PCI... do our stuff.. }
  2056.  
  2057.  
  2058.   begin
  2059.     if not installermode then
  2060.     begin
  2061.  
  2062.       if not ioredirected then textmode(co80+font8x8);
  2063.       writeln(' Craig Hart''s PCI+AGP bus sniffer, version ',revision,', freeware made in 1996-2005.');
  2064.       writeln;
  2065.       write('PCI BIOS Version ',PCIverhi,'.',wrhex(PCIverlo),' found!');
  2066.  
  2067.       if summary then writeln('                                  (Summary Report)') else
  2068.  
  2069.       writeln;
  2070.       writeln('Number of PCI Busses : ',PCI_hibus+1);
  2071.       write('PCI Characteristics  : ');
  2072.       if PCIchar and 1=1 then write('Config Mechanism 1 ') else usebios:=true; { must use BIOS if no cfg mech 1 supported }
  2073.       if PCIchar and 2=2 then write('Config Mechanism 2 ');
  2074.       if PCIchar and 16=16 then write('Special Cycle Mechanism 1 ');
  2075.       if PCIchar and 32=32 then write('Special Cycle Mechanism 2 ');
  2076.       writeln;
  2077.       writeln;
  2078.       write('Searching for PCI Devices using ');
  2079.       if usebios then writeln('the System BIOS') else writeln('Configuration Mechanism 1');
  2080.       writeln;
  2081.     end;
  2082.  
  2083.  
  2084.  
  2085.  
  2086.     for bus:=0 to pci_hibus do        { fix bugs for 440LX chipset, 2 PCI buses, AGP=1 bus! }
  2087.     begin
  2088.       for deviceid:=0 to $1f do
  2089.       begin
  2090.         func:=0;
  2091.         repeat
  2092.       index:=0;
  2093.       repeat
  2094.         if usebios then info:=lookup_bios(deviceid,func,bus,index) else info:=lookup_hw(deviceid,func,bus,index);
  2095.         infotbl[index]:=info;
  2096.         inc(index);
  2097. {don't try to read cfg-space of non-existant devices: hangs some chipsets!}
  2098.         if index=2 then if (infotbl[0]=$ff) and (infotbl[1]=$ff) then index:=$100;
  2099. {don't read past $3f if in short-info modes; avoids crashing on intolerant hardware!}
  2100.         if index=$40 then if installermode or summary then index:=$100;
  2101.       until (index=$100);
  2102.       if (infotbl[0]<>$ff) or (infotbl[1]<>$ff) then
  2103.       begin
  2104. { remember CardBus stuff for later; skip if far bus=0 (i.e. unconfigured) }
  2105.         if (infotbl[$e] and $7f=2) and (infotbl[$19]<>0) then
  2106.         begin
  2107.           cardbus[cardptr+1]:=infotbl[$19];
  2108.           cardptr:=cardptr+1;
  2109.         end;
  2110.         showallinfo;
  2111.       end;
  2112.       inc(func);
  2113. { if func 0 = invalid device, don't test for presence of func 1->7 at all. [$e] isn't valid if [0] and [1] aren't!! }
  2114.           if (func=1) and (infotbl[0]=$ff) and (infotbl[1]=$ff) then func:=8;
  2115. { If not multi-device device, then don't test for func 1-7 as some cards
  2116. incorrectly answer back on all 8 function numbers!!! S3 trio64, for example - stupid!  }    
  2117.           if (func=1) and (infotbl[$e] and $80=0) then func:=8;
  2118.         until func=8; {func }
  2119.       end; {dev }
  2120.     end; { bus }
  2121.  
  2122.  
  2123.  
  2124. { now scan any CardBus busses that weren't included in the BIOS bus count }
  2125.  
  2126.  
  2127.  
  2128.     if cardptr>0 then
  2129.     begin
  2130.       for cbu:=1 to cardptr do        { scan all cardbus busses }
  2131.       begin
  2132.     bus:=cardbus[cbu];
  2133.     if bus>pci_hibus then        { but only those the BIOS hasn't already had us scan }
  2134.     begin
  2135.       for deviceid:=0 to $1f do
  2136.       begin
  2137.         func:=0;
  2138.         repeat
  2139.           index:=0;
  2140.           repeat
  2141.                 if usebios then info:=lookup_bios(deviceid,func,bus,index) else info:=lookup_hw(deviceid,func,bus,index);
  2142.         infotbl[index]:=info;
  2143.         inc(index);
  2144.         if index=2 then if (infotbl[0]=$ff) and (infotbl[1]=$ff) then index:=$100;
  2145.         if index=$40 then if installermode or summary then index:=$100;
  2146.           until (index=$100);
  2147.           if (infotbl[0]<>$ff) or (infotbl[1]<>$ff) then showallinfo;
  2148.           inc(func);
  2149.           if (func=1) and (infotbl[0]=$ff) and (infotbl[1]=$ff) then func:=8;
  2150.           if (func=1) and (infotbl[$e] and $80=0) then func:=8;
  2151.         until func=8;
  2152.       end;
  2153.     end;
  2154.       end;
  2155.     end;
  2156.  
  2157. {
  2158.   The following is an experiment with "Get IRQ Routing Info" BIOS function:
  2159.   the avid coder is free to un-comment the code and try it out: I couldn't
  2160.   make much sense out of the information returned myself!
  2161. }
  2162.  
  2163.  
  2164.  
  2165.     if dopcirouting then
  2166.     begin
  2167.  
  2168.  
  2169.       writeln;
  2170.       writeln('PCI slot IRQ mapping information');
  2171.       irqbuff[0]:=lo(1024);
  2172.       irqbuff[1]:=hi(1024);
  2173.  
  2174.       irqbuff[2]:=lo(ofs(irqbuff)+2);
  2175.       irqbuff[3]:=hi(ofs(irqbuff)+2);
  2176.       irqbuff[4]:=lo(seg(irqbuff));
  2177.       irqbuff[5]:=hi(seg(irqbuff));
  2178.  
  2179.  
  2180.       failed:=true;
  2181.  
  2182.  
  2183.       asm
  2184.     push ds
  2185.  
  2186.     mov bx,0
  2187.     mov ax,seg irqbuff
  2188.     mov es,ax
  2189.     mov di,offset irqbuff
  2190.     mov ax,0f000h
  2191.     mov ds,ax
  2192.     mov ax,0b10eh
  2193.  
  2194.     int $1a
  2195.     pop ds
  2196.  
  2197.     mov cx,word ptr es:[di]
  2198.  
  2199.     cmp ah,0
  2200.     jne @exit
  2201.  
  2202.  
  2203.     mov conmap,bx
  2204.     mov len,cx
  2205.     mov failed,false
  2206.  
  2207.       @exit:
  2208.       end;
  2209.  
  2210.  
  2211.       if not failed then
  2212.       begin
  2213.     textcolor(10);
  2214.     writeln(' PCI slot mapping information read successfully');
  2215.     textcolor(7);
  2216.     writeln;
  2217.  
  2218.  
  2219. { hex-dump table }
  2220.     if dumpregs then dohexdump;
  2221.  
  2222. {}
  2223.     writeln(' PCI slot IRQ availability listing');
  2224.     writeln;
  2225.     for i:=0 to (len shr 4)-1 do
  2226.     begin
  2227.       writeln('  PCI Bus ',irqbuff[2+(i*16)],', Device ',irqbuff[3+(i*16)] shr 3,', Slot ',wrhex(irqbuff[16+(i*16)]));
  2228.       listmap(irqbuff[6+(i*16)] shl 8 + irqbuff[5+(i*16)],'   INTA# can be connected to IRQs ');
  2229.       listmap(irqbuff[9+(i*16)] shl 8 + irqbuff[8+(i*16)],'   INTB# can be connected to IRQs ');
  2230.       listmap(irqbuff[12+(i*16)] shl 8 + irqbuff[11+(i*16)],'   INTC# can be connected to IRQs ');
  2231.       listmap(irqbuff[15+(i*16)] shl 8 + irqbuff[14+(i*16)],'   INTD# can be connected to IRQs ');
  2232.       writeln;
  2233.     end;
  2234.     writeln;
  2235.  
  2236.  
  2237. {}
  2238.     writeln(' PCI slot INTx to IRQ-router mappings');
  2239.     writeln;
  2240.     writeln('  SLOT BUS DEV  INTA INTB INTC INTD');
  2241.     for i:=0 to (len shr 4)-1 do
  2242.     begin
  2243.       write('   ',wrhex(irqbuff[16+(i*16)]),'  ',irqbuff[2+(i*16)]:2,'  ',irqbuff[3+(i*16)] shr 3:2);
  2244. {      write('   ',irqbuff[3+(i*16)] and 3);}
  2245.       write('    ',wrhex(irqbuff[4+(i*16)]),'   ',wrhex(irqbuff[7+(i*16)]),'   ',
  2246.         wrhex(irqbuff[10+(i*16)]),'   ',wrhex(irqbuff[13+(i*16)]),'  ');
  2247.  
  2248.       if usebios then
  2249.       begin
  2250.         infotbl[0]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],0);
  2251.         infotbl[1]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],1);
  2252.         infotbl[2]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],2);
  2253.         infotbl[3]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],3);
  2254.         infotbl[4]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],4);
  2255.         infotbl[5]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],5);
  2256.         infotbl[6]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],6);
  2257.         infotbl[7]:=lookup_bios(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],7);
  2258.       end else
  2259.       begin
  2260.         infotbl[0]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],0);
  2261.         infotbl[1]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],1);
  2262.         infotbl[2]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],2);
  2263.         infotbl[3]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],3);
  2264.         infotbl[4]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],4);
  2265.         infotbl[5]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],5);
  2266.         infotbl[6]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],6);
  2267.         infotbl[7]:=lookup_hw(irqbuff[3+(i*16)] shr 3,irqbuff[3+(i*16)] and 3,irqbuff[2+(i*16)],7);
  2268.       end;
  2269.  
  2270.       cmpstr:=wrhexw(infotbl[1] shl 8+infotbl[0]);
  2271.       if cmpstr<>'FFFF' then
  2272.       begin
  2273.         lookupven(true);
  2274.         cmpstr:=wrhexw(infotbl[3] shl 8+infotbl[2]);
  2275.         lookupdev;
  2276.       end else write('No Device Detected');
  2277.  
  2278.  
  2279.  
  2280.  
  2281.       writeln;
  2282.     end;
  2283.     writeln;
  2284.  
  2285.  
  2286. {}
  2287.     listmap(conmap,' IRQ''s dedicated to PCI : ');
  2288.  
  2289.       end else
  2290.       begin
  2291.     textcolor(12);
  2292.     writeln(' Unable to read slot mapping information from PCI BIOS!');
  2293.     textcolor(7);
  2294.       end;
  2295.       writeln;
  2296.     end;
  2297.  
  2298.  
  2299.  
  2300.  
  2301.  
  2302. { BIOS IRQ Routing table tests }
  2303.  
  2304.     if dorouting then
  2305.     begin
  2306.       showroutinginfo;
  2307.     end;
  2308.  
  2309.  
  2310.  
  2311.  
  2312.  
  2313. { summarial IRQ info }
  2314.  
  2315.     if not installermode then
  2316.     begin
  2317.       writeln;
  2318.       write('IRQ Summary: ');
  2319.       failed:=true;
  2320.       disp:=0;
  2321.       for i:=0 to 15 do if irqmap[i]>0 then inc(disp); { count IRQs}
  2322.       for i:=0 to 15 do if irqmap[i]>0 then
  2323.       begin
  2324.     if failed then
  2325.     begin
  2326.       if disp=1 then write('IRQ ') else write('IRQs ');
  2327.     end else write(',');
  2328.     write(i);
  2329.     failed:=false;
  2330.       end;
  2331.       if failed then writeln('No IRQ''s are used by PCI Devices!') else
  2332.       begin
  2333.     if disp=1 then write(' is') else write(' are');
  2334.     writeln(' used by PCI devices');
  2335.       end;
  2336.  
  2337.       write('Shared IRQs: ');
  2338.       failed:=true;
  2339.       for i:=0 to 15 do if irqmap[i]>1 then
  2340.       begin
  2341.     if not failed then write('             ');
  2342.     writeln('IRQ ',i,' is shared by ',irqmap[i],' PCI Devices');
  2343.     failed:=false;
  2344.       end;
  2345.       if failed then writeln('There are no shared PCI IRQs');
  2346.     end;
  2347.     
  2348.  
  2349.  
  2350.  
  2351. { walk the tree }
  2352.  
  2353. count:=0;
  2354.  
  2355. for bus:=0 to pci_hibus do
  2356. begin
  2357.   for deviceid:=0 to $1f do
  2358.   begin
  2359.     func:=0;
  2360.     repeat
  2361.       index:=0;
  2362.       repeat
  2363.         info:=lookup_hw(deviceid,func,bus,index);
  2364.     infotbl[index]:=info;
  2365.     inc(index);
  2366. {don't try to read cfg-space of non-existant devices: hangs some chipsets!}
  2367.     if index=2 then if (infotbl[0]=$ff) and (infotbl[1]=$ff) then index:=$40;
  2368.       until (index=$40); { index }
  2369.  
  2370.       if (infotbl[0]<>$ff) or (infotbl[1]<>$ff) then
  2371.       begin
  2372. { we have a device ! }
  2373.         tree[count].b:=bus;
  2374.         tree[count].d:=deviceid;
  2375.         tree[count].f:=func;
  2376.         tree[count].vid:=infotbl[1] shl 8 + infotbl[0];
  2377.         tree[count].did:=infotbl[3] shl 8 + infotbl[2];
  2378.         inc(count);
  2379.       end;
  2380.  
  2381.       inc(func);
  2382. { if func 0 = invalid device, don't test for presence of func 1->7 at all. [$e] isn't valid if [0] and [1] aren't!! }
  2383.       if (func=1) and (infotbl[0]=$ff) and (infotbl[1]=$ff) then func:=8;
  2384. { If not multi-device device, then don't test for func 1-7 as some cards
  2385. incorrectly answer back on all 8 function numbers!!! S3 trio64, for example - stupid!  }    
  2386.       if (func=1) and (infotbl[$e] and $80=0) then func:=8;
  2387.     until func=8; {func }
  2388.   end; {dev }
  2389. end; { bus }
  2390.  
  2391.  
  2392.  
  2393.  
  2394.  
  2395.  
  2396. if showtree and not installermode then
  2397. begin
  2398.   writeln;
  2399.   writeln;
  2400.   writeln('PCI Busses, Devices and Device Functions Tree');
  2401.   writeln;
  2402.   olb:=$ff;
  2403.   lastd:=tree[count-1].d;
  2404.   lastb:=tree[count-1].b;
  2405.   
  2406.   for i:=count to 199 do tree[i].d:=$ff;                                { flag unused }
  2407.  
  2408.   for i:=0 to count-1 do
  2409.   begin
  2410.     if tree[i].b<>olb then writeln('──Bus ',tree[i].b);                            { bus }
  2411.     if (tree[i].d=lastd) and (tree[i].b=lastb) and (tree[i].f=0) then write('   └──') else        { final tree item }
  2412.     if tree[i].f=0 then write('   ├──') else write('   │  ');                        { single F or MF dev }
  2413.     
  2414. { up to here colums before "device" }
  2415. { after here colums for func }
  2416.  
  2417.     if tree[i].f=0 then write(' Device ',wrhex(tree[i].d),'h');                        { device }
  2418.  
  2419.     if (tree[i].f=0) and (tree[i+1].d=tree[i].d) then
  2420.     begin
  2421.       writeln('');                                              { MF func 0 only }
  2422.       write('   │     ├── Function ',tree[i].f);
  2423.     end;
  2424.     if tree[i].f>0 then if tree[i+1].d=tree[i].d then
  2425.       write('   ├── Function ',tree[i].f) else write('   └── Function ',tree[i].f);        { func bar }
  2426.     write(' - ',wrhexw(tree[i].vid),'h:',wrhexw(tree[i].did),'h  ');
  2427. {    
  2428. uncomment this to display the device names
  2429.  
  2430.     cmpstr:=wrhexw(tree[i].vid);
  2431.     lookupven(true);
  2432.     cmpstr:=wrhexw(tree[i].did);
  2433.     lookupdev;
  2434. }
  2435.     writeln;
  2436.     
  2437.     olb:=tree[i].b;
  2438.   end;
  2439.   writeln;
  2440.   writeln('A total of ',count-1,' Devices Found');
  2441.   writeln;
  2442.   writeln;
  2443. end;  
  2444.  
  2445.   
  2446.     
  2447.     
  2448.   end;
  2449. end.
  2450.  
  2451.